﻿Option Explicit On
Option Strict On

Imports Microsoft.SPOT
Imports Gadgeteer.Interfaces
Imports System.Threading
Imports Gadgeteer

Namespace NetMF.Bluetooth
    Public Class Bluetooth
        Inherits Gadgeteer.Modules.Module

        Private serialPort As Serial
        Private reset As DigitalOutput
        Private statusInt As InterruptInput
        Private readerThread As Thread

        Private _localAddress As String
        Public ReadOnly Property localAddress As String
            Get
                Return _localAddress
            End Get
        End Property

        Private _currentState As BluetoothState
        Public ReadOnly Property currentState As BluetoothState
            Get
                Return _currentState
            End Get
        End Property

        Public Enum BluetoothState
            Initializing = 0
            Ready = 1
            Inquiring = 2
            Connecting = 3
            Connected = 4
            Disconnected = 5
        End Enum

        Public Sub New(socketNumber As Integer)
            Me.New(socketNumber, 9600)
        End Sub

        Public Sub New(socketNumber As Integer, baud As Integer)
            Dim socket As Socket = Gadgeteer.Socket.GetSocket(socketNumber, True, Me, Nothing)

            reset = New DigitalOutput(socket, Gadgeteer.Socket.Pin.Six, False, Me)
            statusInt = New InterruptInput(socket, socket.Pin.Three, GlitchFilterMode.Off, ResistorMode.Disabled, InterruptMode.RisingAndFallingEdge, Me)
            serialPort = New Serial(socket, 9600, Serial.SerialParity.None, Serial.SerialStopBits.One, 8, Serial.HardwareFlowControl.NotRequired, Me)

            serialPort.ReadTimeout = Timeout.Infinite
            serialPort.Open()
            System.Threading.Thread.Sleep(10)
            SetBaudRate(9600)
            reset.Write(True)

            With serialPort
                SetBaudRate(baud)
                .Flush()
                .Close()
                System.Threading.Thread.Sleep(100)
                .BaudRate = baud
                System.Threading.Thread.Sleep(100)
                .Open()
            End With

            readerThread = New System.Threading.Thread(New ThreadStart(AddressOf runReaderThread))
            readerThread.Start()

            Dim timerGetLocalAddress As New Gadgeteer.Timer(2000, Gadgeteer.Timer.BehaviorType.RunOnce)
            AddHandler timerGetLocalAddress.Tick, AddressOf GetLocalAddress
            timerGetLocalAddress.Start()
        End Sub

        Public Sub ResetDevice()
            reset.Write(False)
            System.Threading.Thread.Sleep(10)
            reset.Write(True)
        End Sub

        Public Sub SetBaudRate(baud As Integer)
            reset.Write(False)
            SendCommad(String.Concat("+STBD=", baud.ToString))
        End Sub

        Private Sub runReaderThread()
            Debug.Print("Start reader")
            While True
                Dim response As String = ""
                Dim curChar As Integer = 0
                Dim count_byte As Integer = serialPort.BytesToRead
                If count_byte > 0 Then
                    System.Threading.Thread.Sleep(30)
                    While serialPort.BytesToRead > 0 AndAlso curChar <> 10
                        curChar = serialPort.ReadByte
                        If curChar = 13 OrElse curChar = 10 Then Continue While
                        response = String.Concat(response, CChar(Strings.ChrW(curChar)))
                    End While

                    If response <= "" Then Continue While
                    Dim atCmd As String = ExtractCommonResult(response)
                    Select Case atCmd
                        Case "+BTADR"
                            Dim start As Integer = response.IndexOf(atCmd) + atCmd.Length
                            Dim len As Integer = response.Length - start
                            _localAddress = response.Substring(start, len)
                            Continue While
                        Case "+BTSTATE:"
                            Dim start As Integer = response.IndexOf(atCmd) + atCmd.Length
                            Dim len As Integer = response.Length - start
                            _currentState = CType(CInt(response.Substring(start, len)), BluetoothState)
                            OnBluetoothStateChanged(Me, _currentState)
                            Continue While
                        Case "+INPIN:"
                            OnPinRequested(Me)
                            Continue While
                        Case "+RTINQ="
                            Dim macAddress As String = ""
                            Dim name As String = ""
                            OnDeviceInquired(Me, macAddress, name)
                            Continue While
                    End Select

                    OnDataReceived(Me, response)
                    Debug.Print("From bluetooth: " + response)
                End If

            End While
        End Sub

        Private Function ExtractCommonResult(response As String) As String
            If response.IndexOf("+BTADR") > -1 Then Return "+BTADR"
            If response.IndexOf("+BTSTATE:") > -1 Then Return "+BTSTATE:"
            If response.IndexOf("+INPIN:") > -1 Then Return "+INPIN:"
            If response.IndexOf("+RTINQ=") > -1 Then Return "+RTINQ="
            Return ""
        End Function

        Public Sub SetDeviceName(name As String)
            SendCommad(String.Concat("+STNA=", name))
        End Sub

        Public Sub SetPinCode(pinCode As String)
            SendCommad(String.Concat("+STPIN=", pinCode))
        End Sub

        Public Sub DeletePinCode(pinCode As String)
            SendCommad("+DLTPIN")
        End Sub

        Public Sub EnterPairingMode()
            SendCommad(String.Concat("+INQ=", "1"))
        End Sub

        Public Sub InputPinCode(pinCode As String)
            SendCommad(String.Concat("+RTPIN=", pinCode))
        End Sub

        Public Sub SetLastPairedAutoConnect(enable As Boolean)
            If enable Then
                SendCommad(String.Concat("+STAUTO=", "1"))
            Else
                SendCommad(String.Concat("+STAUTO=", "0"))
            End If
        End Sub

        Public Sub SetLastPairedConnectMe(enable As Boolean)
            If enable Then
                SendCommad(String.Concat("+STOAUT=", "1"))
            Else
                SendCommad(String.Concat("+STOAUT=", "0"))
            End If
        End Sub

        Private Sub GetLocalAddress(timer As Gadgeteer.Timer)
            timer.Stop()
            SendCommad("+RTADDR")
        End Sub

        Private Sub SendCommad(command As String)
            serialPort.Write(String.Concat(Microsoft.VisualBasic.Constants.vbCrLf, _
                                           command, _
                                           Microsoft.VisualBasic.Constants.vbCrLf))
        End Sub

        Public Sub SendMessage(message As String)
            serialPort.WriteLine(message)
        End Sub

        Public Delegate Sub BluetoothStateChangedHandler(ByVal sender As Bluetooth, ByVal btState As BluetoothState)
        Private onBluetoothStateChangedCaller As BluetoothStateChangedHandler
        Public Event BluetoothStateChanged As BluetoothStateChangedHandler
        Protected Overridable Sub OnBluetoothStateChanged(ByVal sender As Bluetooth, ByVal btState As BluetoothState)
            If (Me.onBluetoothStateChangedCaller Is Nothing) Then
                Me.onBluetoothStateChangedCaller = New BluetoothStateChangedHandler(AddressOf Me.OnBluetoothStateChanged)
            End If
            If Program.CheckAndInvoke(Me.onBluetoothStateChangedCaller, Me.onBluetoothStateChangedCaller, New Object() {sender, btState}) Then
                RaiseEvent BluetoothStateChanged(sender, btState)
            End If
        End Sub

        Public Delegate Sub PinRequestedHandler(ByVal sender As Bluetooth)
        Private onPinRequestedCaller As PinRequestedHandler
        Public Event PinRequested As PinRequestedHandler

        Protected Overridable Sub OnPinRequested(ByVal sender As Bluetooth)
            If (Me.onPinRequestedCaller Is Nothing) Then
                Me.onPinRequestedCaller = New PinRequestedHandler(AddressOf Me.OnPinRequested)
            End If
            If Program.CheckAndInvoke(Me.onPinRequestedCaller, Me.onPinRequestedCaller, New Object() {sender}) Then
                RaiseEvent PinRequested(sender)
            End If
        End Sub

        Public Delegate Sub DataReceivedHandler(ByVal sender As Bluetooth, ByVal data As String)
        Private onDataReceivedCaller As DataReceivedHandler
        Public Event DataReceived As DataReceivedHandler

        Protected Overridable Sub OnDataReceived(ByVal sender As Bluetooth, ByVal data As String)
            If (Me.onDataReceivedCaller Is Nothing) Then
                Me.onDataReceivedCaller = New DataReceivedHandler(AddressOf Me.OnDataReceived)
            End If
            If Program.CheckAndInvoke(Me.onDataReceivedCaller, Me.onDataReceivedCaller, New Object() {sender, data}) Then
                RaiseEvent DataReceived(sender, data)
            End If
        End Sub

        Public Delegate Sub DeviceInquiredHandler(ByVal sender As Bluetooth, ByVal macAddress As String, ByVal name As String)
        Private onDeviceInquiredCaller As DeviceInquiredHandler
        Public Event DeviceInquired As DeviceInquiredHandler

        Protected Overridable Sub OnDeviceInquired(ByVal sender As Bluetooth, ByVal macAddress As String, ByVal name As String)
            If (Me.onDeviceInquiredCaller Is Nothing) Then
                Me.onDeviceInquiredCaller = New DeviceInquiredHandler(AddressOf Me.OnDeviceInquired)
            End If
            If Program.CheckAndInvoke(Me.onDeviceInquiredCaller, Me.onDeviceInquiredCaller, New Object() {sender, macAddress, name}) Then
                RaiseEvent DeviceInquired(sender, macAddress, name)
            End If
        End Sub

        Public Class Client
            Private bluetooth As Bluetooth

            Friend Sub New(bluetooth As Bluetooth)
                Debug.Print("Client Mode")
                Me.bluetooth = bluetooth
                bluetooth.SendCommad(String.Concat("+STWMOD=", "0"))
            End Sub

            Public Sub EnterPairingMode()
                Debug.Print("Enter Pairing Mode")
                bluetooth.SendCommad(String.Concat("+INQ=", "1"))
            End Sub

            Public Sub InputPinCode(pinCode As String)
                Debug.Print(String.Concat("Inputting pin: ", pinCode))
                bluetooth.SendCommad(String.Concat("+RTPIN=", pinCode))
            End Sub

            Public Sub Disconnect()
                Debug.Print("Disconnection is not working...")
                Throw New System.NotSupportedException("Disconnection is not working")
            End Sub

            Public Sub Send(message As String)
                bluetooth.SendMessage(message)
            End Sub
        End Class

        Public Class Host
            Private bluetooth As Bluetooth

            Friend Sub New(bluetooth As Bluetooth)
                Debug.Print("Host mode")
                Me.bluetooth = bluetooth
                bluetooth.SendCommad(String.Concat("+STWMOD=", "1"))
            End Sub

            Public Sub InquireDevice()
                bluetooth.SendCommad(String.Concat("+INQ=", "1"))
            End Sub

            Public Sub Connect(macAddress As String)
                bluetooth.SendCommad(String.Concat("+CONN=", macAddress))
            End Sub

            Public Sub InputPinCode(pinCode As String)
                Debug.Print(String.Concat("Inputting pin: ", pinCode))
                bluetooth.SendCommad(String.Concat("+RTPIN=", pinCode))
            End Sub

            Public Sub Disconnect()
                Debug.Print("Disconnection is not working...")
                Throw New System.NotSupportedException("Disconnection is not working")
            End Sub
        End Class
    End Class
End Namespace